//
// Created by Administrator on 2022/1/14.
//

#include "Utils.h"

std::string Utils::ReadShaderSource(const char* filePath)
{
    std::string source;
    std::stringstream ss;
    std::ifstream ifs;
    ifs.exceptions(std::ifstream::badbit | std::ifstream::failbit);
    try
    {
        ifs.open(filePath);
        ss << ifs.rdbuf();
        ifs.close();
        source = ss.str();
    }
    catch(const std::ifstream::failure& e)
    {
        std::cerr << e.what() << std::endl;
    }
    return source;
}

void Utils::PrintShaderLog(GLuint shader, const char* name)
{
    GLint compiled;
    glGetShaderiv(shader, GL_COMPILE_STATUS, &compiled);
    if(!compiled)
    {
        std::cout << name << " compilation failed" << std::endl;
        int len = 0;
        glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &len);
        if(len > 0)
        {
            char* log = new char[len];
            int chWritten = 0;
            glGetShaderInfoLog(shader, len, &chWritten, log);
            std::cout << "Shader Info Log: " << log << std::endl;
            delete[] log;
        }
    }
}

void Utils::PrintProgramLog(GLuint program)
{
    GLint linked;
    glGetProgramiv(program, GL_LINK_STATUS, &linked);
    if(!linked)
    {
        std::cout << "linking failed" << std::endl;
        int len = 0;
        glGetProgramiv(program, GL_INFO_LOG_LENGTH, &len);
        if(len > 0)
        {
            char* log = new char[len];
            int chWritten = 0;
            glGetProgramInfoLog(program, len, &chWritten, log);
            std::cout << "Program Info Log: " << log << std::endl;
            delete[] log;
        }
    }
}

bool Utils::CheckOpenGLError()
{
    bool foundError = false;
    int glErr;
    while((glErr = glGetError()) != GL_NO_ERROR)
    {
        std::cout << "glError: " << glErr << std::endl;
        foundError = true;
    }
    return foundError;
}

GLuint Utils::CreateShaderProgram(const char* vp, const char* fp)
{
    std::string vShaderStr = ReadShaderSource(vp);
    std::string fShaderStr = ReadShaderSource(fp);

    GLuint vShader = glCreateShader(GL_VERTEX_SHADER);
    GLuint fShader = glCreateShader(GL_FRAGMENT_SHADER);

    const char* vShaderSrc = vShaderStr.c_str();
    const char* fShaderSrc = fShaderStr.c_str();
    glShaderSource(vShader, 1, &vShaderSrc, nullptr);
    glShaderSource(fShader, 1, &fShaderSrc, nullptr);
    glCompileShader(vShader);
    CheckOpenGLError();
    PrintShaderLog(vShader, "vertex");
    glCompileShader(fShader);
    CheckOpenGLError();
    PrintShaderLog(fShader, "fragment");
    GLuint vfProgram = glCreateProgram();
    glAttachShader(vfProgram, vShader);
    glAttachShader(vfProgram, fShader);
    glLinkProgram(vfProgram);
    CheckOpenGLError();
    PrintProgramLog(vfProgram);
    glDeleteShader(vShader);
    glDeleteShader(fShader);
    return vfProgram;
}

GLuint Utils::LoadTexture(const char *texImagePath)
{
    GLuint texture;
    texture = SOIL_load_OGL_texture(texImagePath, SOIL_LOAD_AUTO, SOIL_CREATE_NEW_ID, SOIL_FLAG_INVERT_Y);
    if(texture == 0) std::cout << "could not find texture file: " << texImagePath << std::endl;
    glBindTexture(GL_TEXTURE_2D, texture);
    // GL_NEAREST_MIPMAP_NEAREST  no filter
    // GL_LINEAR_MIPMAP_NEAREST linear
    // GL_NEAREST_MIPMAP_LINEAR bilinear
    // GL_LINEAR_MIPMAP_LINEAR trilinear
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
    glGenerateMipmap(GL_TEXTURE_2D);

    // anisotropic filter 各向异性过滤（AF），和标准的mipmap采样正方形不同，它采样
    // 的是宽高不同的矩形，这样在宽和高方向上就可以有不同的过滤等级，这就是各向异性的意思
    // 这个特性需要查询显卡是否支持
    if(glfwExtensionSupported("GL_EXT_texture_filter_anisotropic"))
    {
        GLfloat anisoSetting = 0.0f;
        glGetFloatv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &anisoSetting);
        glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, anisoSetting);
    }

    // 设置wrap
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);

    return texture;
}

float Utils::Deg2Rad(float deg)
{
    return deg * DEG_2_RAD;
}

float Utils::Rad2Deg(float rad)
{
    return rad * RAD_2_DEG;
}

float* Utils::GoldAmbient()
{
    static float a[4] = {0.2473f, 0.1995f, 0.0745f, 1.0f};
    return a;
}

float* Utils::GoldDiffuse()
{
    static float d[4] = {0.7516f, 0.6065f, 0.2265f, 1.0f};
    return d;
}

float* Utils::GoldSpecular()
{
    static float s[4] = {0.6283f, 0.5558f, 0.3661f, 1.0f};
    return s;
}

float Utils::GoldShininess()
{
    return 51.200f;
}

float* Utils::SilverAmbient()
{
    static float a[4] = {0.1923f, 0.1923f, 0.1923f, 1.0f};
    return a;
}

float* Utils::SilverDiffuse()
{
    static float d[4] = {0.5075f, 0.5075f, 0.5075f, 1.0f};
    return d;
}

float* Utils::SilverSpecular()
{
    static float s[4] = {0.5083f, 0.5083f, 0.5083f, 1.0f};
    return s;
}

float Utils::SilverShininess()
{
    return 51.200f;
}

float* Utils::BronzeAmbient()
{
    static float a[4] = {0.2125f, 0.1275f, 0.0540f, 1.0f};
    return a;
}

float* Utils::BronzeDiffuse()
{
    static float d[4] = {0.7140f, 0.4284f, 0.1814f, 1.0f};
    return d;
}

float* Utils::BronzeSpecular()
{
    static float s[4] = {0.3935f, 0.2719f, 0.1667f, 1.0f};
    return s;
}

float Utils::BronzeShininess()
{
    return 25.6f;
}

float* Utils::JadeAmbient()
{
    static float a[4] = {0.1350f, 0.2225f, 0.1575f, 0.95f};
    return a;
}

float* Utils::JadeDiffuse()
{
    static float d[4] = {0.5400f, 0.8900f, 0.6300f, 0.95f};
    return d;
}

float* Utils::JadeSpecular()
{
    static float s[4] = {0.3162f, 0.3162f, 0.3162f, 0.95f};
    return s;
}

float Utils::JadeShininess()
{
    return 12.800f;
}

float* Utils::PearlAmbient()
{
    static float a[4] = {0.2500f, 0.2073f, 0.2073f, 0.922f};
    return a;
}

float* Utils::PearlDiffuse()
{
    static float d[4] = {1.0000f, 0.8290f, 0.8290f, 0.922f};
    return d;
}

float* Utils::PearlSpecular()
{
    static float s[4] = {0.2966f, 0.2966f, 0.2966f, 0.922f};
    return s;
}

float Utils::PearlShininess()
{
    return 11.264;
}